2.2.1. Package¶

Một số package được sử dụng

In [1]:
# BASE
# ------------------------------------------------------
import numpy as np
import pandas as pd
import os
import gc
import warnings

# PACF - ACF
# ------------------------------------------------------
import statsmodels.api as sm

# DATA VISUALIZATION
# ------------------------------------------------------
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px


# CONFIGURATIONS
# ------------------------------------------------------
pd.set_option('display.max_columns', None)
pd.options.display.float_format = '{:.2f}'.format
warnings.filterwarnings('ignore')
2.2.2. Nhập dữ liệu¶
In [2]:
# Import
train = pd.read_csv("D:/2021.2/store-sales-time-series-forecasting/train.csv")
test = pd.read_csv("D:/2021.2/store-sales-time-series-forecasting/test.csv")
stores = pd.read_csv("D:/2021.2/store-sales-time-series-forecasting/stores.csv")
#sub = pd.read_csv("../input/store-sales-time-series-forecasting/sample_submission.csv")   
transactions = pd.read_csv("D:/2021.2/store-sales-time-series-forecasting/transactions.csv").sort_values(["store_nbr", "date"])


# Datetime
train["date"] = pd.to_datetime(train.date)
test["date"] = pd.to_datetime(test.date)
transactions["date"] = pd.to_datetime(transactions.date)

# Data types
train.onpromotion = train.onpromotion.astype("float16")
train.sales = train.sales.astype("float32")
stores.cluster = stores.cluster.astype("int8")

train.head()
Out[2]:
id date store_nbr family sales onpromotion
0 0 2013-01-01 1 AUTOMOTIVE 0.00 0.00
1 1 2013-01-01 1 BABY CARE 0.00 0.00
2 2 2013-01-01 1 BEAUTY 0.00 0.00
3 3 2013-01-01 1 BEVERAGES 0.00 0.00
4 4 2013-01-01 1 BOOKS 0.00 0.00

2.2.3. Giao dịch¶

Bắt đầu bằng các dữ liệu về giao dịch (Transactions)

In [3]:
transactions.head(10)
Out[3]:
date store_nbr transactions
1 2013-01-02 1 2111
47 2013-01-03 1 1833
93 2013-01-04 1 1863
139 2013-01-05 1 1509
185 2013-01-06 1 520
231 2013-01-07 1 1807
277 2013-01-08 1 1869
323 2013-01-09 1 1910
369 2013-01-10 1 1679
415 2013-01-11 1 1813

Thuộc tính này có mối tương quan cao với doanh số bán hàng nhưng trước tiên, ta phải tổng hợp tính năng bán hàng để tìm mối quan hệ. Giao dịch có nghĩa là có bao nhiêu người đến cửa hàng hoặc bao nhiêu hóa đơn được tạo trong một ngày.

Doanh số bán hàng cho biết tổng doanh số bán hàng cho một nhóm sản phẩm tại một cửa hàng cụ thể vào một ngày nhất định. Giá trị phân số là có thể vì sản phẩm có thể được bán theo đơn vị phân số (ví dụ: 1,5 kg pho mát, trái ngược với 1 túi khoai tây chiên).

Đó là lý do tại sao, các giao dịch sẽ là một trong những tính năng có liên quan trong mô hình. Trong các phần sau, ta sẽ tạo các tính năng mới bằng cách sử dụng các giao dịch.

In [4]:
temp = pd.merge(train.groupby(["date", "store_nbr"]).sales.sum().reset_index(), transactions, how = "left")
print("Spearman Correlation between Total Sales and Transactions: {:,.4f}".format(temp.corr("spearman").sales.loc["transactions"]))
px.line(transactions.sort_values(["store_nbr", "date"]), x='date', y='transactions', color='store_nbr',title = "Transactions" )
Spearman Correlation between Total Sales and Transactions: 0.8175

Ta thấy Giao dịch có một mô hình ổn định. Tất cả các tháng đều tương tự nhau ngoại trừ tháng 12 từ năm 2013 đến năm 2017 theo hình hộp. Ngoài ra, chúng ta vừa thấy một mẫu tương tự cho mỗi cửa hàng trong phần trước. Doanh thu của cửa hàng luôn tăng vào cuối năm.

In [5]:
a = transactions.copy()
a["year"] = a.date.dt.year
a["month"] = a.date.dt.month
px.box(a, x="year", y="transactions" , color = "month", title = "Transactions")

Hãy xem xét các giao dịch bằng cách sử dụng doanh số bán hàng trung bình hàng tháng

Ta vừa tìm ra một mô hình giúp tăng doanh số bán hàng. Đó là cuối năm. Chúng ta có thể thấy rằng các giao dịch tăng vào mùa xuân và giảm sau mùa xuân.

In [6]:
a = transactions.set_index("date").resample("M").transactions.mean().reset_index()
a["year"] = a.date.dt.year
px.line(a, x='date', y='transactions', color='year',title = "Monthly Average Transactions" )

Khi xem xét mối quan hệ của chúng, ta có thể thấy rằng có mối tương quan cao giữa tổng doanh số bán hàng và giao dịch.

In [7]:
px.scatter(temp, x = "transactions", y = "sales", trendline = "ols", trendline_color_override = "red")

Các ngày trong tuần rất quan trọng cho việc mua sắm. Các cửa hàng giao dịch nhiều hơn vào cuối tuần. Hầu hết, các mẫu đều giống nhau từ năm 2013 đến năm 2017 và thứ Bảy là ngày quan trọng nhất để mua sắm.

In [8]:
a = transactions.copy()
a["year"] = a.date.dt.year
a["dayofweek"] = a.date.dt.dayofweek+1
a = a.groupby(["year", "dayofweek"]).transactions.mean().reset_index()
px.line(a, x="dayofweek", y="transactions" , color = "year", title = "Transactions")

2.2.4. Giá dầu¶

Nền kinh tế là một trong những vấn đề lớn nhất đối với chính phủ và người dân. Nó ảnh hưởng đến tất cả mọi thứ theo cách tốt hoặc xấu. Trong trường hợp ở đây, Ecuador là một quốc gia phụ thuộc vào dầu mỏ. Thay đổi giá dầu ở Ecuador sẽ gây ra sự khác biệt trong mô hình.

Có một số dữ liệu điểm bị thiếu trong dữ liệu dầu hàng ngày như có thể thấy bên dưới. Ta có thể xử lý dữ liệu bằng cách sử dụng các phương pháp imputation khác nhau. Tuy nhiên, ở đây tôi đã chọn một giải pháp đơn giản. Nội suy tuyến tính phù hợp với chuỗi thời gian này. Ta có thể thấy xu hướng và dự đoán các dữ liệu điểm còn thiếu khi nhìn vào biểu đồ thời gian của giá dầu.

In [9]:
# Import 
oil = pd.read_csv("D:/2021.2/store-sales-time-series-forecasting/oil.csv")
oil["date"] = pd.to_datetime(oil.date)
# Resample
oil = oil.set_index("date").dcoilwtico.resample("D").sum().reset_index()
# Interpolate
oil["dcoilwtico"] = np.where(oil["dcoilwtico"] == 0, np.nan, oil["dcoilwtico"])
oil["dcoilwtico_interpolated"] =oil.dcoilwtico.interpolate()
# Plot
p = oil.melt(id_vars=['date']+list(oil.keys()[5:]), var_name='Legend')
px.line(p.sort_values(["Legend", "date"], ascending = [False, True]), x='date', y='value', color='Legend',title = "Daily Oil Price" )

"Ecuador là một quốc gia phụ thuộc vào dầu mỏ" nhưng sự thật có đúng như vậy không? Chúng ta có thể thực sự thấy điều đó từ dữ liệu bằng cách xem xét không?

Trước hết, chúng ta hãy xem xét các mối tương quan giữa doanh số và giao dịch. Các giá trị tương quan không mạnh nhưng dấu hiệu bán hàng là âm. Có lẽ, chúng ta có thể nắm bắt được một manh mối. Về mặt logic, nếu giá dầu hàng ngày cao, chúng tôi cho rằng nền kinh tế của Ecuador đang xấu và điều đó có nghĩa là giá sản phẩm tăng và doanh số bán hàng giảm, thể hiện một mối quan hệ nghịch biến ở đây.

In [10]:
temp = pd.merge(temp, oil, how = "left")
print("Correlation with Daily Oil Prices")
print(temp.drop(["store_nbr", "dcoilwtico"], axis = 1).corr("spearman").dcoilwtico_interpolated.loc[["sales", "transactions"]], "\n")


fig, axes = plt.subplots(1, 2, figsize = (15,5))
temp.plot.scatter(x = "dcoilwtico_interpolated", y = "transactions", ax=axes[0])
temp.plot.scatter(x = "dcoilwtico_interpolated", y = "sales", ax=axes[1], color = "r")
axes[0].set_title('Daily oil price & Transactions', fontsize = 15)
axes[1].set_title('Daily Oil Price & Sales', fontsize = 15);
Correlation with Daily Oil Prices
sales          -0.30
transactions    0.04
Name: dcoilwtico_interpolated, dtype: float64 

Tuy nhiên, ta không nên quyết định mình sẽ làm gì bằng cách nhìn vào biểu đồ hoặc kết quả. Ta cần xác định các giả thuyết mới.

Sẽ là sai nếu chúng ta chỉ xem xét một số kết quả đầu ra đơn giản như trên và ta đã nói rằng không có mối quan hệ nào với giá dầu và quyết định không sử dụng dữ liệu giá dầu.

Ta sẽ phân tích sâu hơn nữa. Hãy vẽ một biểu đồ phân tán nhưng chúng ta hãy chú ý đến các họ sản phẩm lần này. Tất cả các ô gần như chứa cùng một mẫu. Khi giá dầu hàng ngày dưới 70, có nhiều doanh số bán hàng hơn trong dữ liệu. Có 2 cụm ở đây. Họ trên 70 và dưới 70. Thực ra thì có vẻ khá dễ hiểu.

Đây có thể là một cách tốt. Vừa rồi, chúng tôi không thể thấy mô hình giá dầu hàng ngày, nhưng bây giờ chúng tôi đã trích xuất một mô hình mới từ đó.

In [11]:
a = pd.merge(train.groupby(["date", "family"]).sales.sum().reset_index(), oil.drop("dcoilwtico", axis = 1), how = "left")
c = a.groupby("family").corr("spearman").reset_index()
c = c[c.level_1 == "dcoilwtico_interpolated"][["family", "sales"]].sort_values("sales")

fig, axes = plt.subplots(7, 5, figsize = (20,20))
for i, fam in enumerate(c.family):
    if i < 6:
        a[a.family == fam].plot.scatter(x = "dcoilwtico_interpolated", y = "sales", ax=axes[0, i-1])
        axes[0, i-1].set_title(fam+"\n Correlation:"+str(c[c.family == fam].sales.iloc[0])[:6], fontsize = 12)
        axes[0, i-1].axvline(x=70, color='r', linestyle='--')
    if i >= 6 and i<11:
        a[a.family == fam].plot.scatter(x = "dcoilwtico_interpolated", y = "sales", ax=axes[1, i-6])
        axes[1, i-6].set_title(fam+"\n Correlation:"+str(c[c.family == fam].sales.iloc[0])[:6], fontsize = 12)
        axes[1, i-6].axvline(x=70, color='r', linestyle='--')
    if i >= 11 and i<16:
        a[a.family == fam].plot.scatter(x = "dcoilwtico_interpolated", y = "sales", ax=axes[2, i-11])
        axes[2, i-11].set_title(fam+"\n Correlation:"+str(c[c.family == fam].sales.iloc[0])[:6], fontsize = 12)
        axes[2, i-11].axvline(x=70, color='r', linestyle='--')
    if i >= 16 and i<21:
        a[a.family == fam].plot.scatter(x = "dcoilwtico_interpolated", y = "sales", ax=axes[3, i-16])
        axes[3, i-16].set_title(fam+"\n Correlation:"+str(c[c.family == fam].sales.iloc[0])[:6], fontsize = 12)
        axes[3, i-16].axvline(x=70, color='r', linestyle='--')
    if i >= 21 and i<26:
        a[a.family == fam].plot.scatter(x = "dcoilwtico_interpolated", y = "sales", ax=axes[4, i-21])
        axes[4, i-21].set_title(fam+"\n Correlation:"+str(c[c.family == fam].sales.iloc[0])[:6], fontsize = 12)
        axes[4, i-21].axvline(x=70, color='r', linestyle='--')
    if i >= 26 and i < 31:
        a[a.family == fam].plot.scatter(x = "dcoilwtico_interpolated", y = "sales", ax=axes[5, i-26])
        axes[5, i-26].set_title(fam+"\n Correlation:"+str(c[c.family == fam].sales.iloc[0])[:6], fontsize = 12)
        axes[5, i-26].axvline(x=70, color='r', linestyle='--')
    if i >= 31 :
        a[a.family == fam].plot.scatter(x = "dcoilwtico_interpolated", y = "sales", ax=axes[6, i-31])
        axes[6, i-31].set_title(fam+"\n Correlation:"+str(c[c.family == fam].sales.iloc[0])[:6], fontsize = 12)
        axes[6, i-31].axvline(x=70, color='r', linestyle='--')
        
        
plt.tight_layout(pad=5)
plt.suptitle("Daily Oil Product & Total Family Sales \n", fontsize = 20);
plt.show()
        

2.3.1. Doanh thu¶

Mục tiêu chính của ta là dự đoán doanh số bán hàng của cửa hàng cho từng dòng sản phẩm. Vì lý do này, cột bán hàng nên được kiểm tra nghiêm túc hơn. Chúng ta cần tìm hiểu mọi thứ như tính thời vụ, xu hướng, sự bất thường, điểm tương đồng với các chuỗi thời gian khác.

Hầu hết các cửa hàng đều tương tự nhau, khi ta kiểm tra chúng bằng ma trận tương quan. Một số cửa hàng, chẳng hạn như 20, 21, 22 và 52 có thể hơi khác một chút.

In [12]:
a = train[["store_nbr", "sales"]]
a["ind"] = 1
a["ind"] = a.groupby("store_nbr").ind.cumsum().values
a = pd.pivot(a, index = "ind", columns = "store_nbr", values = "sales").corr()
mask = np.triu(a.corr())
plt.figure(figsize=(20, 20))
sns.heatmap(a,
        annot=True,
        fmt='.1f',
        cmap='coolwarm',
        square=True,
        mask=mask,
        linewidths=1,
        cbar=False)
plt.title("Correlations among stores",fontsize = 20)
plt.show()

Dưới đây là biểu đồ cho ta biết doanh thu mỗi ngày

In [13]:
a = train.set_index("date").groupby("store_nbr").resample("D").sales.sum().reset_index()
px.line(a, x = "date", y= "sales", color = "store_nbr", title = "Daily total sales of the stores")

Ta nhận ra một số hàng không cần thiết trong dữ liệu khi đang xem xét chuỗi thời gian của từng cửa hàng một. Nếu chọn các cửa hàng từ trên xuống, một số trong số đó không có doanh số bán hàng vào đầu năm 2013. Ta có thể thấy chúng nếu nhìn vào các cửa hàng 20, 21, 22, 29, 36, 42, 52 và 53. Ta quyết định để loại bỏ các hàng đó trước khi các cửa hàng mở cửa. Trong các mã sau, ta sẽ loại bỏ chúng.

In [14]:
print(train.shape)
train = train[~((train.store_nbr == 52) & (train.date < "2017-04-20"))]
train = train[~((train.store_nbr == 22) & (train.date < "2015-10-09"))]
train = train[~((train.store_nbr == 42) & (train.date < "2015-08-21"))]
train = train[~((train.store_nbr == 21) & (train.date < "2015-07-24"))]
train = train[~((train.store_nbr == 29) & (train.date < "2015-03-20"))]
train = train[~((train.store_nbr == 20) & (train.date < "2015-02-13"))]
train = train[~((train.store_nbr == 53) & (train.date < "2014-05-29"))]
train = train[~((train.store_nbr == 36) & (train.date < "2013-05-09"))]
train.shape
(3000888, 6)
Out[14]:
(2780316, 6)

2.3.2. Dự báo bằng 0 (Zero Forecasting)¶

Một số cửa hàng không bán một số họ sản phẩm. Trong đoạn code sau, ta có thể thấy sản phẩm nào không được bán ở những cửa hàng nào. Không khó để dự báo chúng trong 15 ngày tới. Dự báo của họ phải là 0 trong 15 ngày tới.

Ta sẽ xóa chúng khỏi dữ liệu và tạo khung dữ liệu mới cho các nhóm sản phẩm không bao giờ bán. Sau đó, khi chúng ta đang ở phần nộp hồ sơ, ta sẽ kết hợp khung dữ liệu đó với các dự đoán của chúng ta.

In [15]:
c = train.groupby(["store_nbr", "family"]).sales.sum().reset_index().sort_values(["family","store_nbr"])
c = c[c.sales == 0]
c
Out[15]:
store_nbr family sales
1 1 BABY CARE 0.00
397 13 BABY CARE 0.00
727 23 BABY CARE 0.00
1420 44 BABY CARE 0.00
1453 45 BABY CARE 0.00
1486 46 BABY CARE 0.00
1519 47 BABY CARE 0.00
1552 48 BABY CARE 0.00
1585 49 BABY CARE 0.00
1618 50 BABY CARE 0.00
1651 51 BABY CARE 0.00
1684 52 BABY CARE 0.00
268 9 BOOKS 0.00
301 10 BOOKS 0.00
334 11 BOOKS 0.00
367 12 BOOKS 0.00
400 13 BOOKS 0.00
433 14 BOOKS 0.00
466 15 BOOKS 0.00
499 16 BOOKS 0.00
532 17 BOOKS 0.00
565 18 BOOKS 0.00
598 19 BOOKS 0.00
631 20 BOOKS 0.00
664 21 BOOKS 0.00
697 22 BOOKS 0.00
895 28 BOOKS 0.00
928 29 BOOKS 0.00
961 30 BOOKS 0.00
994 31 BOOKS 0.00
1027 32 BOOKS 0.00
1060 33 BOOKS 0.00
1093 34 BOOKS 0.00
1126 35 BOOKS 0.00
1159 36 BOOKS 0.00
1258 39 BOOKS 0.00
1291 40 BOOKS 0.00
1390 43 BOOKS 0.00
1687 52 BOOKS 0.00
1753 54 BOOKS 0.00
514 16 LADIESWEAR 0.00
811 25 LADIESWEAR 0.00
910 28 LADIESWEAR 0.00
943 29 LADIESWEAR 0.00
1042 32 LADIESWEAR 0.00
1075 33 LADIESWEAR 0.00
1141 35 LADIESWEAR 0.00
1306 40 LADIESWEAR 0.00
1405 43 LADIESWEAR 0.00
1768 54 LADIESWEAR 0.00
449 14 LAWN AND GARDEN 0.00
977 30 LAWN AND GARDEN 0.00
1769 54 LAWN AND GARDEN 0.00
In [16]:
print(train.shape)
# Anti Join
outer_join = train.merge(c[c.sales == 0].drop("sales",axis = 1), how = 'outer', indicator = True)
train = outer_join[~(outer_join._merge == 'both')].drop('_merge', axis = 1)
del outer_join
gc.collect()
train.shape
(2780316, 6)
Out[16]:
(2698648, 6)
In [17]:
zero_prediction = []
for i in range(0,len(c)):
    zero_prediction.append(
        pd.DataFrame({
            "date":pd.date_range("2017-08-16", "2017-08-31").tolist(),
            "store_nbr":c.store_nbr.iloc[i],
            "family":c.family.iloc[i],
            "sales":0
        })
    )
zero_prediction = pd.concat(zero_prediction)
del c
gc.collect()
zero_prediction
Out[17]:
date store_nbr family sales
0 2017-08-16 1 BABY CARE 0
1 2017-08-17 1 BABY CARE 0
2 2017-08-18 1 BABY CARE 0
3 2017-08-19 1 BABY CARE 0
4 2017-08-20 1 BABY CARE 0
... ... ... ... ...
11 2017-08-27 54 LAWN AND GARDEN 0
12 2017-08-28 54 LAWN AND GARDEN 0
13 2017-08-29 54 LAWN AND GARDEN 0
14 2017-08-30 54 LAWN AND GARDEN 0
15 2017-08-31 54 LAWN AND GARDEN 0

848 rows × 4 columns

2.3.3. Kiểm tra các loại sản phẩm có hoạt động hay không¶

Một số sản phẩm hiếm khi có thể bán được trong các cửa hàng. Ta sẽ xem xét trong 60 ngày qua.

Một số loại sản phẩm phụ thuộc vào thời vụ. Một số trong số đó có thể không hoạt động trong 60 ngày qua nhưng không có nghĩa là nó không hoạt động.

In [18]:
c = train.groupby(["family", "store_nbr"]).tail(60).groupby(["family", "store_nbr"]).sales.sum().reset_index()
c[c.sales == 0]
Out[18]:
family store_nbr sales
54 BABY CARE 2 0.00
62 BABY CARE 10 0.00
64 BABY CARE 12 0.00
65 BABY CARE 14 0.00
76 BABY CARE 26 0.00
82 BABY CARE 32 0.00
83 BABY CARE 33 0.00
87 BABY CARE 37 0.00
88 BABY CARE 38 0.00
92 BABY CARE 42 0.00
95 BABY CARE 54 0.00
205 BOOKS 2 0.00
206 BOOKS 3 0.00
208 BOOKS 5 0.00
209 BOOKS 6 0.00
214 BOOKS 25 0.00
215 BOOKS 26 0.00
217 BOOKS 37 0.00
219 BOOKS 41 0.00
220 BOOKS 42 0.00
229 BOOKS 53 0.00
1014 LADIESWEAR 36 0.00
1039 LAWN AND GARDEN 10 0.00
1041 LAWN AND GARDEN 12 0.00
1042 LAWN AND GARDEN 13 0.00
1043 LAWN AND GARDEN 15 0.00
1044 LAWN AND GARDEN 16 0.00
1045 LAWN AND GARDEN 17 0.00
1047 LAWN AND GARDEN 19 0.00
1050 LAWN AND GARDEN 22 0.00
1059 LAWN AND GARDEN 32 0.00
1060 LAWN AND GARDEN 33 0.00
1062 LAWN AND GARDEN 35 0.00
1621 SCHOOL AND OFFICE SUPPLIES 1 0.00
1622 SCHOOL AND OFFICE SUPPLIES 2 0.00
1626 SCHOOL AND OFFICE SUPPLIES 6 0.00
1636 SCHOOL AND OFFICE SUPPLIES 16 0.00
1643 SCHOOL AND OFFICE SUPPLIES 23 0.00
1644 SCHOOL AND OFFICE SUPPLIES 24 0.00
1652 SCHOOL AND OFFICE SUPPLIES 32 0.00
1653 SCHOOL AND OFFICE SUPPLIES 33 0.00
1660 SCHOOL AND OFFICE SUPPLIES 40 0.00
1674 SCHOOL AND OFFICE SUPPLIES 54 0.00

Như ta có thể thấy bên dưới, những ví dụ này quá hiếm và doanh số bán hàng cũng thấp. Ta sẽ tạo ra một tính năng mới. Nó cho thấy rằng họ sản phẩm đang hoạt động hay không.

In [19]:
fig, ax = plt.subplots(1,5, figsize = (20,4))
train[(train.store_nbr == 10) & (train.family == "LAWN AND GARDEN")].set_index("date").sales.plot(ax = ax[0], title = "STORE 10 - LAWN AND GARDEN")
train[(train.store_nbr == 36) & (train.family == "LADIESWEAR")].set_index("date").sales.plot(ax = ax[1], title = "STORE 36 - LADIESWEAR")
train[(train.store_nbr == 6) & (train.family == "SCHOOL AND OFFICE SUPPLIES")].set_index("date").sales.plot(ax = ax[2], title = "STORE 6 - SCHOOL AND OFFICE SUPPLIES")
train[(train.store_nbr == 14) & (train.family == "BABY CARE")].set_index("date").sales.plot(ax = ax[3], title = "STORE 14 - BABY CARE")
train[(train.store_nbr == 53) & (train.family == "BOOKS")].set_index("date").sales.plot(ax = ax[4], title = "STORE 43 - BOOKS")
plt.show()

Chúng ta có thể nắm bắt được xu hướng, tính thời vụ và sự bất thường của các họ.

In [20]:
a = train.set_index("date").groupby("family").resample("D").sales.sum().reset_index()
px.line(a, x = "date", y= "sales", color = "family", title = "Daily total sales of the family")

Ta quan sát các cửa hàng. Có rất nhiều sản phẩm trong các cửa hàng và chúng ta cần biết họ sản phẩm nào bán nhiều hơn. Chúng ta hãy làm một mô hình để xem điều đó.

Biểu đồ cho chúng ta thấy GROCERY I và BEVERAGES là những gia đình bán chạy nhất.

In [21]:
a = train.groupby("family").sales.mean().sort_values(ascending = False).reset_index()
px.bar(a, y = "family", x="sales", color = "family", title = "Which product family preferred more?")

Các cửa hàng có thể khác nhau như thế nào? Ta thực sự chưa tìm thấy một mô hình chính cho các cửa hàng. Nhưng ta mới chỉ quan sát một biểu đồ duy nhất. Có thể có một số mô hình tiềm ẩn mà ta chưa thấy.

In [22]:
d = pd.merge(train, stores)
d["store_nbr"] = d["store_nbr"].astype("int8")
d["year"] = d.date.dt.year
px.line(d.groupby(["city", "year"]).sales.mean().reset_index(), x = "year", y = "sales", color = "city")

2.3.4. Sự kiện và dịp lễ¶

Dữ liệu ngày lễ và sự kiện chứa rất nhiều thông tin bên trong. Nó là một siêu dữ liệu nên ta phải phân chia nó một cách hợp lý và làm cho dữ liệu trở nên hữu ích.

Vấn đề của chúng ta là gì?

  • Một số ngày lễ quốc gia đã được thay đổi.
  • Có thể có một vài sự kiện, dịp lễ trong một ngày. Khi ta hợp nhất tất cả dữ liệu, số hàng có thể tăng lên. Ta không muốn trùng lặp.
  • Phạm vi của ngày lễ là gì? Nó có thể là khu vực hoặc quốc gia hoặc địa phương. Ta cần chia chúng theo phạm vi.
  • Vấn đề ngày làm việc
  • Một số sự kiện cụ thể
  • Tạo các thuộc tính mới...
In [23]:
holidays = pd.read_csv("D:/2021.2/store-sales-time-series-forecasting/holidays_events.csv")
holidays["date"] = pd.to_datetime(holidays.date)

# holidays[holidays.type == "Holiday"]
# holidays[(holidays.type == "Holiday") & (holidays.transferred == True)]

# Transferred Holidays
tr1 = holidays[(holidays.type == "Holiday") & (holidays.transferred == True)].drop("transferred", axis = 1).reset_index(drop = True)
tr2 = holidays[(holidays.type == "Transfer")].drop("transferred", axis = 1).reset_index(drop = True)
tr = pd.concat([tr1,tr2], axis = 1)
tr = tr.iloc[:, [5,1,2,3,4]]

holidays = holidays[(holidays.transferred == False) & (holidays.type != "Transfer")].drop("transferred", axis = 1)
holidays = holidays.append(tr).reset_index(drop = True)


# Additional Holidays
holidays["description"] = holidays["description"].str.replace("-", "").str.replace("+", "").str.replace('\d+', '')
holidays["type"] = np.where(holidays["type"] == "Additional", "Holiday", holidays["type"])

# Bridge Holidays
holidays["description"] = holidays["description"].str.replace("Puente ", "")
holidays["type"] = np.where(holidays["type"] == "Bridge", "Holiday", holidays["type"])

 
# Work Day Holidays, that is meant to payback the Bridge.
work_day = holidays[holidays.type == "Work Day"]  
holidays = holidays[holidays.type != "Work Day"]  


# Split

# Events are national
events = holidays[holidays.type == "Event"].drop(["type", "locale", "locale_name"], axis = 1).rename({"description":"events"}, axis = 1)

holidays = holidays[holidays.type != "Event"].drop("type", axis = 1)
regional = holidays[holidays.locale == "Regional"].rename({"locale_name":"state", "description":"holiday_regional"}, axis = 1).drop("locale", axis = 1).drop_duplicates()
national = holidays[holidays.locale == "National"].rename({"description":"holiday_national"}, axis = 1).drop(["locale", "locale_name"], axis = 1).drop_duplicates()
local = holidays[holidays.locale == "Local"].rename({"description":"holiday_local", "locale_name":"city"}, axis = 1).drop("locale", axis = 1).drop_duplicates()



d = pd.merge(train.append(test), stores)
d["store_nbr"] = d["store_nbr"].astype("int8")


# National Holidays & Events
#d = pd.merge(d, events, how = "left")
d = pd.merge(d, national, how = "left")
# Regional
d = pd.merge(d, regional, how = "left", on = ["date", "state"])
# Local
d = pd.merge(d, local, how = "left", on = ["date", "city"])

# Work Day: It will be removed when real work day colum created
d = pd.merge(d,  work_day[["date", "type"]].rename({"type":"IsWorkDay"}, axis = 1),how = "left")

# EVENTS
events["events"] =np.where(events.events.str.contains("futbol"), "Futbol", events.events)

def one_hot_encoder(df, nan_as_category=True):
    original_columns = list(df.columns)
    categorical_columns = df.select_dtypes(["category", "object"]).columns.tolist()
    # categorical_columns = [col for col in df.columns if df[col].dtype == 'object']
    df = pd.get_dummies(df, columns=categorical_columns, dummy_na=nan_as_category)
    new_columns = [c for c in df.columns if c not in original_columns]
    df.columns = df.columns.str.replace(" ", "_")
    return df, df.columns.tolist()

events, events_cat = one_hot_encoder(events, nan_as_category=False)
events["events_Dia_de_la_Madre"] = np.where(events.date == "2016-05-08", 1,events["events_Dia_de_la_Madre"])
events = events.drop(239)

d = pd.merge(d, events, how = "left")
d[events_cat] = d[events_cat].fillna(0)

# New features
d["holiday_national_binary"] = np.where(d.holiday_national.notnull(), 1, 0)
d["holiday_local_binary"] = np.where(d.holiday_local.notnull(), 1, 0)
d["holiday_regional_binary"] = np.where(d.holiday_regional.notnull(), 1, 0)

# 
d["national_independence"] = np.where(d.holiday_national.isin(['Batalla de Pichincha',  'Independencia de Cuenca', 'Independencia de Guayaquil', 'Independencia de Guayaquil', 'Primer Grito de Independencia']), 1, 0)
d["local_cantonizacio"] = np.where(d.holiday_local.str.contains("Cantonizacio"), 1, 0)
d["local_fundacion"] = np.where(d.holiday_local.str.contains("Fundacion"), 1, 0)
d["local_independencia"] = np.where(d.holiday_local.str.contains("Independencia"), 1, 0)


holidays, holidays_cat = one_hot_encoder(d[["holiday_national","holiday_regional","holiday_local"]], nan_as_category=False)
d = pd.concat([d.drop(["holiday_national","holiday_regional","holiday_local"], axis = 1),holidays], axis = 1)

he_cols = d.columns[d.columns.str.startswith("events")].tolist() + d.columns[d.columns.str.startswith("holiday")].tolist() + d.columns[d.columns.str.startswith("national")].tolist()+ d.columns[d.columns.str.startswith("local")].tolist()
d[he_cols] = d[he_cols].astype("int8")

d[["family", "city", "state", "type"]] = d[["family", "city", "state", "type"]].astype("category")

del holidays, holidays_cat, work_day, local, regional, national, events, events_cat, tr, tr1, tr2, he_cols
gc.collect()

d.head(10)
Out[23]:
id date store_nbr family sales onpromotion city state type cluster IsWorkDay events_Black_Friday events_Cyber_Monday events_Dia_de_la_Madre events_Futbol events_Terremoto_Manabi holiday_national_binary holiday_local_binary holiday_regional_binary national_independence local_cantonizacio local_fundacion local_independencia holiday_national_Batalla_de_Pichincha holiday_national_Carnaval holiday_national_Dia_de_Difuntos holiday_national_Dia_de_la_Madre holiday_national_Dia_del_Trabajo holiday_national_Independencia_de_Cuenca holiday_national_Independencia_de_Guayaquil holiday_national_Navidad holiday_national_Primer_Grito_de_Independencia holiday_national_Primer_dia_del_ano holiday_national_Viernes_Santo holiday_regional_Provincializacion_Santa_Elena holiday_regional_Provincializacion_de_Cotopaxi holiday_regional_Provincializacion_de_Imbabura holiday_regional_Provincializacion_de_Santo_Domingo holiday_local_Cantonizacion_de_Cayambe holiday_local_Cantonizacion_de_El_Carmen holiday_local_Cantonizacion_de_Guaranda holiday_local_Cantonizacion_de_Latacunga holiday_local_Cantonizacion_de_Libertad holiday_local_Cantonizacion_de_Quevedo holiday_local_Cantonizacion_de_Riobamba holiday_local_Cantonizacion_de_Salinas holiday_local_Cantonizacion_del_Puyo holiday_local_Fundacion_de_Ambato holiday_local_Fundacion_de_Cuenca holiday_local_Fundacion_de_Esmeraldas holiday_local_Fundacion_de_Guayaquil holiday_local_Fundacion_de_Ibarra holiday_local_Fundacion_de_Loja holiday_local_Fundacion_de_Machala holiday_local_Fundacion_de_Manta holiday_local_Fundacion_de_Quito holiday_local_Fundacion_de_Riobamba holiday_local_Fundacion_de_Santo_Domingo holiday_local_Independencia_de_Ambato holiday_local_Independencia_de_Guaranda holiday_local_Independencia_de_Latacunga
0 0 2013-01-01 1 AUTOMOTIVE 0.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 1782 2013-01-02 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
2 3564 2013-01-03 1 AUTOMOTIVE 3.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
3 5346 2013-01-04 1 AUTOMOTIVE 3.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
4 7128 2013-01-05 1 AUTOMOTIVE 5.00 0.00 Quito Pichincha D 13 Work Day 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
5 8910 2013-01-06 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
6 10692 2013-01-07 1 AUTOMOTIVE 0.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
7 12474 2013-01-08 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
8 14256 2013-01-09 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
9 16038 2013-01-10 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 NaN 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Áp dụng bài test A/B cho các thuộc tính Sự kiện và Ngày lễ. Chúng có ý nghĩa thống kê không? Ngoài ra, nó có thể là một cách tốt để lựa chọn tính năng đầu tiên.

  • H0: Doanh số bằng nhau (M1 = M2)
  • H1: Doanh số không bằng nhau (M1! = M2)
In [24]:
def AB_Test(dataframe, group, target):
    
    # Packages
    from scipy.stats import shapiro
    import scipy.stats as stats
    
    # Split A/B
    groupA = dataframe[dataframe[group] == 1][target]
    groupB = dataframe[dataframe[group] == 0][target]
    
    # Assumption: Normality
    ntA = shapiro(groupA)[1] < 0.05
    ntB = shapiro(groupB)[1] < 0.05
    # H0: Distribution is Normal! - False
    # H1: Distribution is not Normal! - True
    
    if (ntA == False) & (ntB == False): # "H0: Normal Distribution"
        # Parametric Test
        # Assumption: Homogeneity of variances
        leveneTest = stats.levene(groupA, groupB)[1] < 0.05
        # H0: Homogeneity: False
        # H1: Heterogeneous: True
        
        if leveneTest == False:
            # Homogeneity
            ttest = stats.ttest_ind(groupA, groupB, equal_var=True)[1]
            # H0: M1 == M2 - False
            # H1: M1 != M2 - True
        else:
            # Heterogeneous
            ttest = stats.ttest_ind(groupA, groupB, equal_var=False)[1]
            # H0: M1 == M2 - False
            # H1: M1 != M2 - True
    else:
        # Non-Parametric Test
        ttest = stats.mannwhitneyu(groupA, groupB)[1] 
        # H0: M1 == M2 - False
        # H1: M1 != M2 - True
        
    # Result
    temp = pd.DataFrame({
        "AB Hypothesis":[ttest < 0.05], 
        "p-value":[ttest]
    })
    temp["Test Type"] = np.where((ntA == False) & (ntB == False), "Parametric", "Non-Parametric")
    temp["AB Hypothesis"] = np.where(temp["AB Hypothesis"] == False, "Fail to Reject H0", "Reject H0")
    temp["Comment"] = np.where(temp["AB Hypothesis"] == "Fail to Reject H0", "A/B groups are similar!", "A/B groups are not similar!")
    temp["Feature"] = group
    temp["GroupA_mean"] = groupA.mean()
    temp["GroupB_mean"] = groupB.mean()
    temp["GroupA_median"] = groupA.median()
    temp["GroupB_median"] = groupB.median()
    
    # Columns
    if (ntA == False) & (ntB == False):
        temp["Homogeneity"] = np.where(leveneTest == False, "Yes", "No")
        temp = temp[["Feature","Test Type", "Homogeneity","AB Hypothesis", "p-value", "Comment", "GroupA_mean", "GroupB_mean", "GroupA_median", "GroupB_median"]]
    else:
        temp = temp[["Feature","Test Type","AB Hypothesis", "p-value", "Comment", "GroupA_mean", "GroupB_mean", "GroupA_median", "GroupB_median"]]
    
    # Print Hypothesis
    # print("# A/B Testing Hypothesis")
    # print("H0: A == B")
    # print("H1: A != B", "\n")
    
    return temp
    
# Apply A/B Testing
he_cols = d.columns[d.columns.str.startswith("events")].tolist() + d.columns[d.columns.str.startswith("holiday")].tolist() + d.columns[d.columns.str.startswith("national")].tolist()+ d.columns[d.columns.str.startswith("local")].tolist()
ab = []
for i in he_cols:
    ab.append(AB_Test(dataframe=d[d.sales.notnull()], group = i, target = "sales"))
ab = pd.concat(ab)
ab
Out[24]:
Feature Test Type AB Hypothesis p-value Comment GroupA_mean GroupB_mean GroupA_median GroupB_median
0 events_Black_Friday Non-Parametric Reject H0 0.00 A/B groups are not similar! 393.70 396.72 26.58 19.00
0 events_Cyber_Monday Non-Parametric Reject H0 0.00 A/B groups are not similar! 472.65 396.57 24.00 19.00
0 events_Dia_de_la_Madre Non-Parametric Reject H0 0.00 A/B groups are not similar! 386.35 396.74 17.00 19.00
0 events_Futbol Non-Parametric Reject H0 0.00 A/B groups are not similar! 360.30 397.01 13.00 19.00
0 events_Terremoto_Manabi Non-Parametric Reject H0 0.00 A/B groups are not similar! 519.38 394.30 30.00 19.00
0 holiday_national_binary Non-Parametric Reject H0 0.00 A/B groups are not similar! 471.40 393.15 20.00 19.00
0 holiday_local_binary Non-Parametric Reject H0 0.00 A/B groups are not similar! 472.89 396.41 24.00 19.00
0 holiday_regional_binary Non-Parametric Reject H0 0.00 A/B groups are not similar! 254.61 396.76 11.00 19.00
0 holiday_national_Batalla_de_Pichincha Non-Parametric Fail to Reject H0 0.62 A/B groups are similar! 434.22 396.60 21.00 19.00
0 holiday_national_Carnaval Non-Parametric Fail to Reject H0 0.19 A/B groups are similar! 373.36 396.86 19.00 19.00
0 holiday_national_Dia_de_Difuntos Non-Parametric Reject H0 0.00 A/B groups are not similar! 471.98 396.49 26.00 19.00
0 holiday_national_Dia_de_la_Madre Non-Parametric Reject H0 0.00 A/B groups are not similar! 495.64 396.42 30.00 19.00
0 holiday_national_Dia_del_Trabajo Non-Parametric Reject H0 0.00 A/B groups are not similar! 535.81 396.30 23.00 19.00
0 holiday_national_Independencia_de_Cuenca Non-Parametric Reject H0 0.00 A/B groups are not similar! 528.54 396.40 23.37 19.00
0 holiday_national_Independencia_de_Guayaquil Non-Parametric Reject H0 0.00 A/B groups are not similar! 476.40 396.52 27.00 19.00
0 holiday_national_Navidad Non-Parametric Reject H0 0.00 A/B groups are not similar! 607.37 394.18 36.49 19.00
0 holiday_national_Primer_Grito_de_Independencia Non-Parametric Reject H0 0.02 A/B groups are not similar! 407.43 396.68 22.00 19.00
0 holiday_national_Primer_dia_del_ano Non-Parametric Reject H0 0.00 A/B groups are not similar! 347.77 397.00 0.00 19.00
0 holiday_national_Viernes_Santo Non-Parametric Reject H0 0.00 A/B groups are not similar! 327.01 396.92 16.00 19.00
0 holiday_regional_Provincializacion_Santa_Elena Non-Parametric Fail to Reject H0 0.20 A/B groups are similar! 188.21 396.72 11.50 19.00
0 holiday_regional_Provincializacion_de_Cotopaxi Non-Parametric Reject H0 0.00 A/B groups are not similar! 245.20 396.73 5.00 19.00
0 holiday_regional_Provincializacion_de_Imbabura Non-Parametric Reject H0 0.01 A/B groups are not similar! 182.07 396.72 9.00 19.00
0 holiday_regional_Provincializacion_de_Santo_Do... Non-Parametric Fail to Reject H0 0.60 A/B groups are similar! 326.72 396.72 19.50 19.00
0 holiday_local_Cantonizacion_de_Cayambe Non-Parametric Fail to Reject H0 0.11 A/B groups are similar! 459.53 396.71 24.50 19.00
0 holiday_local_Cantonizacion_de_El_Carmen Non-Parametric Fail to Reject H0 0.07 A/B groups are similar! 235.88 396.72 17.50 19.00
0 holiday_local_Cantonizacion_de_Guaranda Non-Parametric Reject H0 0.01 A/B groups are not similar! 183.58 396.72 8.00 19.00
0 holiday_local_Cantonizacion_de_Latacunga Non-Parametric Reject H0 0.00 A/B groups are not similar! 188.05 396.73 12.00 19.00
0 holiday_local_Cantonizacion_de_Libertad Non-Parametric Fail to Reject H0 0.28 A/B groups are similar! 297.36 396.71 12.00 19.00
0 holiday_local_Cantonizacion_de_Quevedo Non-Parametric Fail to Reject H0 0.13 A/B groups are similar! 213.73 396.72 18.50 19.00
0 holiday_local_Cantonizacion_de_Riobamba Non-Parametric Reject H0 0.00 A/B groups are not similar! 195.55 396.72 8.00 19.00
0 holiday_local_Cantonizacion_de_Salinas Non-Parametric Fail to Reject H0 0.80 A/B groups are similar! 285.65 396.72 19.50 19.00
0 holiday_local_Cantonizacion_del_Puyo Non-Parametric Fail to Reject H0 0.33 A/B groups are similar! 150.41 396.72 23.00 19.00
0 holiday_local_Fundacion_de_Ambato Non-Parametric Fail to Reject H0 0.87 A/B groups are similar! 336.04 396.72 21.60 19.00
0 holiday_local_Fundacion_de_Cuenca Non-Parametric Fail to Reject H0 0.92 A/B groups are similar! 333.86 396.72 19.00 19.00
0 holiday_local_Fundacion_de_Esmeraldas Non-Parametric Fail to Reject H0 0.23 A/B groups are similar! 333.56 396.71 10.00 19.00
0 holiday_local_Fundacion_de_Guayaquil Non-Parametric Reject H0 0.00 A/B groups are not similar! 288.48 396.80 14.00 19.00
0 holiday_local_Fundacion_de_Ibarra Non-Parametric Fail to Reject H0 0.05 A/B groups are similar! 224.81 396.72 7.50 19.00
0 holiday_local_Fundacion_de_Loja Non-Parametric Fail to Reject H0 0.26 A/B groups are similar! 441.52 396.71 20.00 19.00
0 holiday_local_Fundacion_de_Machala Non-Parametric Fail to Reject H0 0.50 A/B groups are similar! 297.36 396.72 17.00 19.00
0 holiday_local_Fundacion_de_Manta Non-Parametric Fail to Reject H0 0.63 A/B groups are similar! 255.17 396.72 15.00 19.00
0 holiday_local_Fundacion_de_Quito Non-Parametric Reject H0 0.00 A/B groups are not similar! 732.59 396.15 41.00 19.00
0 holiday_local_Fundacion_de_Riobamba Non-Parametric Reject H0 0.00 A/B groups are not similar! 193.41 396.72 3.00 19.00
0 holiday_local_Fundacion_de_Santo_Domingo Non-Parametric Fail to Reject H0 0.42 A/B groups are similar! 314.92 396.72 21.00 19.00
0 holiday_local_Independencia_de_Ambato Non-Parametric Fail to Reject H0 0.13 A/B groups are similar! 370.56 396.71 29.50 19.00
0 holiday_local_Independencia_de_Guaranda Non-Parametric Fail to Reject H0 0.06 A/B groups are similar! 219.94 396.72 6.00 19.00
0 holiday_local_Independencia_de_Latacunga Non-Parametric Reject H0 0.00 A/B groups are not similar! 178.59 396.73 11.00 19.00
0 national_independence Non-Parametric Reject H0 0.00 A/B groups are not similar! 457.03 396.07 23.00 19.00
0 local_cantonizacio Non-Parametric Reject H0 0.00 A/B groups are not similar! 396.33 506.69 19.00 26.00
0 local_fundacion Non-Parametric Reject H0 0.00 A/B groups are not similar! 396.82 250.79 19.00 14.00
0 local_independencia Non-Parametric Reject H0 0.00 A/B groups are not similar! 396.38 486.04 19.00 25.00
In [25]:
d.groupby(["family","events_Futbol"]).sales.mean()[:60]
Out[25]:
family                   events_Futbol
AUTOMOTIVE               0                  6.59
                         1                  6.20
BABY CARE                0                  0.15
                         1                  0.09
BEAUTY                   0                  4.02
                         1                  2.89
BEVERAGES                0               2577.73
                         1               2243.45
BOOKS                    0                  0.15
                         1                  0.00
BREAD/BAKERY             0                500.48
                         1                451.53
CELEBRATION              0                  9.06
                         1                  6.43
CLEANING                 0               1157.94
                         1               1102.08
DAIRY                    0                765.46
                         1                759.26
DELI                     0                286.35
                         1                264.20
EGGS                     0                185.05
                         1                180.72
FROZEN FOODS             0                167.31
                         1                134.66
GROCERY I                0               4079.68
                         1               3695.02
GROCERY II               0                 23.30
                         1                 22.24
HARDWARE                 0                  1.23
                         1                  1.22
HOME AND KITCHEN I       0                 22.16
                         1                 13.94
HOME AND KITCHEN II      0                 18.14
                         1                  6.19
HOME APPLIANCES          0                  0.49
                         1                  0.44
HOME CARE                0                190.43
                         1                159.24
LADIESWEAR               0                  9.54
                         1                  9.70
LAWN AND GARDEN          0                  6.94
                         1                  5.05
LINGERIE                 0                  7.74
                         1                  8.83
LIQUOR,WINE,BEER         0                 91.76
                         1                115.06
MAGAZINES                0                  3.18
                         1                  0.49
MEATS                    0                368.91
                         1                376.27
PERSONAL CARE            0                292.03
                         1                274.46
PET SUPPLIES             0                  4.24
                         1                  2.72
PLAYERS AND ELECTRONICS  0                  6.69
                         1                  4.57
POULTRY                  0                377.97
                         1                424.07
PREPARED FOODS           0                104.43
                         1                106.65
Name: sales, dtype: float32

2.3.5. Các thuộc tính liên quan đến thời gian¶

Ta có thể tạo bao nhiêu tính năng chỉ từ cột ngày? Dưới đây là một ví dụ về các thuộc tính liên quan đến thời gian.

In [26]:
# Time Related Features
def create_date_features(df):
    df['month'] = df.date.dt.month.astype("int8")
    df['day_of_month'] = df.date.dt.day.astype("int8")
    df['day_of_year'] = df.date.dt.dayofyear.astype("int16")
    df['week_of_month'] = (df.date.apply(lambda d: (d.day-1) // 7 + 1)).astype("int8")
    df['week_of_year'] = (df.date.dt.weekofyear).astype("int8")
    df['day_of_week'] = (df.date.dt.dayofweek + 1).astype("int8")
    df['year'] = df.date.dt.year.astype("int32")
    df["is_wknd"] = (df.date.dt.weekday // 4).astype("int8")
    df["quarter"] = df.date.dt.quarter.astype("int8")
    df['is_month_start'] = df.date.dt.is_month_start.astype("int8")
    df['is_month_end'] = df.date.dt.is_month_end.astype("int8")
    df['is_quarter_start'] = df.date.dt.is_quarter_start.astype("int8")
    df['is_quarter_end'] = df.date.dt.is_quarter_end.astype("int8")
    df['is_year_start'] = df.date.dt.is_year_start.astype("int8")
    df['is_year_end'] = df.date.dt.is_year_end.astype("int8")
    # 0: Winter - 1: Spring - 2: Summer - 3: Fall
    df["season"] = np.where(df.month.isin([12,1,2]), 0, 1)
    df["season"] = np.where(df.month.isin([6,7,8]), 2, df["season"])
    df["season"] = pd.Series(np.where(df.month.isin([9, 10, 11]), 3, df["season"])).astype("int8")
    return df
d = create_date_features(d)




# Workday column
d["workday"] = np.where((d.holiday_national_binary == 1) | (d.holiday_local_binary==1) | (d.holiday_regional_binary==1) | (d['day_of_week'].isin([6,7])), 0, 1)
d["workday"] = pd.Series(np.where(d.IsWorkDay.notnull(), 1, d["workday"])).astype("int8")
d.drop("IsWorkDay", axis = 1, inplace = True)

# Wages in the public sector are paid every two weeks on the 15 th and on the last day of the month. 
# Supermarket sales could be affected by this.
d["wageday"] = pd.Series(np.where((d['is_month_end'] == 1) | (d["day_of_month"] == 15), 1, 0)).astype("int8")

d.head(15)
Out[26]:
id date store_nbr family sales onpromotion city state type cluster events_Black_Friday events_Cyber_Monday events_Dia_de_la_Madre events_Futbol events_Terremoto_Manabi holiday_national_binary holiday_local_binary holiday_regional_binary national_independence local_cantonizacio local_fundacion local_independencia holiday_national_Batalla_de_Pichincha holiday_national_Carnaval holiday_national_Dia_de_Difuntos holiday_national_Dia_de_la_Madre holiday_national_Dia_del_Trabajo holiday_national_Independencia_de_Cuenca holiday_national_Independencia_de_Guayaquil holiday_national_Navidad holiday_national_Primer_Grito_de_Independencia holiday_national_Primer_dia_del_ano holiday_national_Viernes_Santo holiday_regional_Provincializacion_Santa_Elena holiday_regional_Provincializacion_de_Cotopaxi holiday_regional_Provincializacion_de_Imbabura holiday_regional_Provincializacion_de_Santo_Domingo holiday_local_Cantonizacion_de_Cayambe holiday_local_Cantonizacion_de_El_Carmen holiday_local_Cantonizacion_de_Guaranda holiday_local_Cantonizacion_de_Latacunga holiday_local_Cantonizacion_de_Libertad holiday_local_Cantonizacion_de_Quevedo holiday_local_Cantonizacion_de_Riobamba holiday_local_Cantonizacion_de_Salinas holiday_local_Cantonizacion_del_Puyo holiday_local_Fundacion_de_Ambato holiday_local_Fundacion_de_Cuenca holiday_local_Fundacion_de_Esmeraldas holiday_local_Fundacion_de_Guayaquil holiday_local_Fundacion_de_Ibarra holiday_local_Fundacion_de_Loja holiday_local_Fundacion_de_Machala holiday_local_Fundacion_de_Manta holiday_local_Fundacion_de_Quito holiday_local_Fundacion_de_Riobamba holiday_local_Fundacion_de_Santo_Domingo holiday_local_Independencia_de_Ambato holiday_local_Independencia_de_Guaranda holiday_local_Independencia_de_Latacunga month day_of_month day_of_year week_of_month week_of_year day_of_week year is_wknd quarter is_month_start is_month_end is_quarter_start is_quarter_end is_year_start is_year_end season workday wageday
0 0 2013-01-01 1 AUTOMOTIVE 0.00 0.00 Quito Pichincha D 13 0 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 2 2013 0 1 1 0 1 0 1 0 0 0 0
1 1782 2013-01-02 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 2 1 1 3 2013 0 1 0 0 0 0 0 0 0 1 0
2 3564 2013-01-03 1 AUTOMOTIVE 3.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 3 3 1 1 4 2013 0 1 0 0 0 0 0 0 0 1 0
3 5346 2013-01-04 1 AUTOMOTIVE 3.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 4 1 1 5 2013 1 1 0 0 0 0 0 0 0 1 0
4 7128 2013-01-05 1 AUTOMOTIVE 5.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 5 5 1 1 6 2013 1 1 0 0 0 0 0 0 0 1 0
5 8910 2013-01-06 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 6 6 1 1 7 2013 1 1 0 0 0 0 0 0 0 0 0
6 10692 2013-01-07 1 AUTOMOTIVE 0.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 7 7 1 2 1 2013 0 1 0 0 0 0 0 0 0 1 0
7 12474 2013-01-08 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 8 8 2 2 2 2013 0 1 0 0 0 0 0 0 0 1 0
8 14256 2013-01-09 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 9 9 2 2 3 2013 0 1 0 0 0 0 0 0 0 1 0
9 16038 2013-01-10 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 10 10 2 2 4 2013 0 1 0 0 0 0 0 0 0 1 0
10 17820 2013-01-11 1 AUTOMOTIVE 3.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 11 11 2 2 5 2013 1 1 0 0 0 0 0 0 0 1 0
11 19602 2013-01-12 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 12 12 2 2 6 2013 1 1 0 0 0 0 0 0 0 1 0
12 21384 2013-01-13 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 13 13 2 2 7 2013 1 1 0 0 0 0 0 0 0 0 0
13 23166 2013-01-14 1 AUTOMOTIVE 2.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 14 14 2 3 1 2013 0 1 0 0 0 0 0 0 0 1 0
14 24948 2013-01-15 1 AUTOMOTIVE 1.00 0.00 Quito Pichincha D 13 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 15 15 3 3 2 2013 0 1 0 0 0 0 0 0 0 1 1

2.3.6. Động đất có ảnh hưởng đến doanh thu của cửa hàng không?¶

Động đất có ảnh hưởng đến doanh thu của cửa hàng không?

Một trận động đất mạnh 7,8 độ Richter đã xảy ra ở Ecuador vào ngày 16 tháng 4 năm 2016. Mọi người tập hợp trong các nỗ lực cứu trợ quyên góp nước và các sản phẩm cần thiết khác, điều này đã ảnh hưởng lớn đến doanh số bán hàng của siêu thị trong vài tuần sau trận động đất.

So sánh doanh số bán hàng trung bình theo năm, tháng và nhóm sản phẩm sẽ là một trong những cách tốt nhất để có thể hiểu trận động đất đã ảnh hưởng đến doanh thu của cửa hàng như thế nào.

Ta có thể sử dụng dữ liệu của tháng 3, tháng 4, tháng 5 và tháng 6 và doanh số bán hàng có thể tăng hoặc giảm đối với một số nhóm sản phẩm.

Cuối cùng, ta trích xuất một cột về động đất từ dữ liệu Ngày lễ và Sự kiện. cột "events_Terremoto_Manabi" sẽ giúp phù hợp với mô hình tốt hơn.

In [27]:
d[(d.month.isin([4,5]))].groupby(["year"]).sales.mean()
Out[27]:
year
2013   249.38
2014   285.53
2015   334.68
2016   489.90
2017   503.50
Name: sales, dtype: float32

Tháng 3¶

In [28]:
pd.pivot_table(d[(d.month.isin([3]))], index="year", columns="family", values="sales", aggfunc="mean")
Out[28]:
family AUTOMOTIVE BABY CARE BEAUTY BEVERAGES BOOKS BREAD/BAKERY CELEBRATION CLEANING DAIRY DELI EGGS FROZEN FOODS GROCERY I GROCERY II HARDWARE HOME AND KITCHEN I HOME AND KITCHEN II HOME APPLIANCES HOME CARE LADIESWEAR LAWN AND GARDEN LINGERIE LIQUOR,WINE,BEER MAGAZINES MEATS PERSONAL CARE PET SUPPLIES PLAYERS AND ELECTRONICS POULTRY PREPARED FOODS PRODUCE SCHOOL AND OFFICE SUPPLIES SEAFOOD
year
2013 5.35 0.00 2.74 1277.85 0.00 416.57 0.00 1055.67 429.53 229.49 157.80 95.06 3281.80 20.69 1.11 0.00 0.00 0.80 0.00 0.00 3.31 9.33 74.50 0.00 368.54 223.88 0.00 0.00 223.56 106.08 2.83 0.00 28.10
2014 6.94 0.06 3.55 3040.63 0.00 446.92 14.74 1215.01 815.26 261.29 189.67 138.42 3807.03 21.25 1.50 23.82 12.52 0.71 297.74 16.65 5.63 8.49 95.85 1.34 382.59 265.75 5.50 11.02 449.17 112.42 2300.86 1.53 27.45
2015 7.55 0.00 2.78 1515.37 0.00 547.43 0.00 1219.26 806.57 329.61 209.24 129.68 4016.91 23.82 1.13 24.85 18.66 0.85 0.00 0.00 5.35 6.52 91.09 0.00 356.97 316.62 0.00 0.00 420.88 104.59 4.51 0.00 28.61
2016 7.34 0.34 4.34 3023.50 0.00 501.95 14.23 1077.72 859.32 277.81 177.58 131.25 4088.61 19.78 1.32 26.11 25.67 0.31 267.45 13.88 5.35 4.92 89.79 6.07 330.77 296.47 5.89 8.72 374.97 109.86 2304.82 1.96 23.89
2017 7.79 0.34 4.29 3733.14 0.59 584.24 14.21 1220.11 991.14 326.49 199.83 130.17 4800.77 22.14 1.45 29.23 29.95 0.71 319.10 14.74 15.97 6.88 89.70 6.28 374.28 331.43 8.79 11.39 395.35 114.03 2442.19 5.02 25.54

Tháng 4 - 5¶

In [29]:
pd.pivot_table(d[(d.month.isin([4,5]))], index="year", columns="family", values="sales", aggfunc="mean")
Out[29]:
family AUTOMOTIVE BABY CARE BEAUTY BEVERAGES BOOKS BREAD/BAKERY CELEBRATION CLEANING DAIRY DELI EGGS FROZEN FOODS GROCERY I GROCERY II HARDWARE HOME AND KITCHEN I HOME AND KITCHEN II HOME APPLIANCES HOME CARE LADIESWEAR LAWN AND GARDEN LINGERIE LIQUOR,WINE,BEER MAGAZINES MEATS PERSONAL CARE PET SUPPLIES PLAYERS AND ELECTRONICS POULTRY PREPARED FOODS PRODUCE SCHOOL AND OFFICE SUPPLIES SEAFOOD
year
2013 5.84 0.00 2.61 1260.96 0.00 418.76 0.00 1027.76 408.36 234.52 154.75 104.79 3308.63 21.95 1.01 0.00 0.00 0.75 0.00 0.00 3.23 9.39 71.10 0.00 377.75 224.65 0.00 0.00 221.59 96.68 5.35 0.00 25.37
2014 5.75 0.00 2.61 1461.64 0.00 416.34 0.00 1028.08 766.63 253.55 184.63 121.35 3614.11 21.44 1.02 0.00 0.00 0.56 0.00 0.00 4.98 8.94 85.83 0.00 361.61 266.44 0.00 0.00 405.27 103.47 5.66 0.00 23.63
2015 6.02 0.00 2.96 1915.16 0.00 551.60 0.00 1227.91 811.31 329.80 198.89 139.64 3989.90 26.46 1.06 26.11 20.73 0.65 127.72 0.00 5.47 8.20 95.21 1.69 365.24 314.39 0.06 0.00 415.49 102.44 5.68 0.00 27.05
2016 7.26 0.32 4.87 3369.27 0.00 551.90 14.25 1218.35 911.86 299.28 186.11 135.95 4898.71 22.04 1.36 27.10 28.28 0.45 315.92 14.96 5.47 6.24 82.89 6.77 363.50 363.47 6.84 10.67 399.37 108.67 2305.70 5.99 24.55
2017 7.63 0.23 5.30 3592.94 0.26 557.84 13.72 1308.01 970.85 323.21 202.54 138.61 4841.85 26.12 1.55 30.31 31.31 0.71 304.85 15.40 19.17 7.83 90.16 6.87 362.41 326.00 9.07 12.22 385.39 96.31 2404.91 11.71 23.17

Tháng 6¶

In [30]:
pd.pivot_table(d[(d.month.isin([6]))], index="year", columns="family", values="sales", aggfunc="mean")
Out[30]:
family AUTOMOTIVE BABY CARE BEAUTY BEVERAGES BOOKS BREAD/BAKERY CELEBRATION CLEANING DAIRY DELI EGGS FROZEN FOODS GROCERY I GROCERY II HARDWARE HOME AND KITCHEN I HOME AND KITCHEN II HOME APPLIANCES HOME CARE LADIESWEAR LAWN AND GARDEN LINGERIE LIQUOR,WINE,BEER MAGAZINES MEATS PERSONAL CARE PET SUPPLIES PLAYERS AND ELECTRONICS POULTRY PREPARED FOODS PRODUCE SCHOOL AND OFFICE SUPPLIES SEAFOOD
year
2013 5.89 0.00 2.72 1297.24 0.00 431.05 0.00 1052.44 419.08 238.23 156.45 120.79 3489.13 21.83 0.95 0.00 0.00 0.71 0.00 0.00 3.43 8.85 75.67 0.00 389.95 216.12 0.00 0.00 232.16 100.72 6.12 0.00 20.99
2014 5.72 0.00 2.46 1458.94 0.00 450.82 0.00 1029.45 757.82 260.01 182.69 122.18 3655.15 22.99 1.03 0.00 0.00 0.41 0.00 0.00 4.35 8.60 94.37 0.00 383.37 270.10 0.00 0.00 409.03 101.04 5.75 0.00 23.97
2015 6.52 0.18 3.79 3178.15 0.00 549.95 14.74 1177.30 829.06 328.15 196.27 139.39 3903.51 25.12 1.30 26.92 31.28 0.12 288.21 16.89 5.27 7.83 101.28 1.53 376.68 297.36 5.87 8.72 411.02 108.23 2346.14 1.33 25.85
2016 6.25 0.35 4.55 2806.97 0.00 550.81 13.96 1108.43 863.92 280.66 182.98 129.32 4170.43 21.84 1.08 28.62 25.08 0.29 260.83 14.07 5.66 4.43 85.12 5.67 350.66 292.74 5.95 8.50 373.23 104.96 2239.03 1.55 23.18
2017 7.38 0.23 5.43 3580.13 0.08 555.64 13.80 1228.70 932.53 317.70 202.11 137.49 4702.43 29.26 1.40 33.93 30.85 0.64 286.55 14.65 17.85 6.78 95.40 6.19 388.38 319.44 9.22 11.34 406.58 90.90 2403.45 1.58 19.36

2.3.7. Hàm tự tương quan (ACF) và hàm tự tương quan riêng từng phần (PACF) cho họ sản phẩm¶

Tính trễ có nghĩa là dịch chuyển thời gian về phía trước một bước hoặc nhiều hơn một bước. Vì vậy, độ trễ có thể sử dụng trong mô hình để cải thiện nó. Tuy nhiên, có bao nhiêu tính trễ bên trong mô hình? Để hiểu điều đó, chúng ta có thể sử dụng ACF và PACF. PACF rất hữu ích để quyết định tính năng nào nên chọn.

Trong bài toán của chúng ta, chúng ta có nhiều chuỗi thời gian và tất nhiên mỗi chuỗi thời gian có các mẫu khác nhau. Bạn biết rằng chuỗi thời gian đó bao gồm các tổ hợp cửa hàng-sản phẩm và chúng tôi có 54 cửa hàng và 33 họ sản phẩm. Chúng tôi không thể kiểm tra tất cả từng cái một. Vì lý do này, tôi sẽ xem xét doanh số trung bình cho từng sản phẩm nhưng nó sẽ độc lập với cửa hàng.

Ngoài ra, dữ liệu thử nghiệm chứa 15 ngày cho mỗi gia đình. Chúng ta nên cẩn thận khi lựa chọn các tính năng có độ trễ. Ta không thể tạo các thuộc tính độ trễ mới từ độ trễ 1 đến độ trễ 15. Thuộc tính này cần được bắt đầu từ 16.

In [31]:
#a = d[d["store_nbr"]==1].set_index("date")
a = d[(d.sales.notnull())].groupby(["date", "family"]).sales.mean().reset_index().set_index("date")
for num, i in enumerate(a.family.unique()):
    try:
        fig, ax = plt.subplots(1,2,figsize=(15,5))
        temp = a[(a.family == i)]#& (a.sales.notnull())
        sm.graphics.tsa.plot_acf(temp.sales, lags=365, ax=ax[0], title = "AUTOCORRELATION\n" + i)
        sm.graphics.tsa.plot_pacf(temp.sales, lags=365, ax=ax[1], title = "PARTIAL AUTOCORRELATION\n" + i)
    except:
        pass

Ta quyết định chọn các độ trễ 16, 20, 30, 45, 365, 730 từ PACF. Ta không biết rằng chúng sẽ giúp cải thiện mô hình nhưng độ trễ thứ 365 và 730 có thể hữu ích. Nếu so sánh giữa năm 2016 và 2017 về doanh số, ta có thể thấy rằng chúng có mối tương quan cao.

In [32]:
a = d[d.year.isin([2016,2017])].groupby(["year", "day_of_year"]).sales.mean().reset_index()
px.line(a, x = "day_of_year", y = "sales", color = "year", title = "Average sales for 2016 and 2017")

2.3.8. Đường trung bình trượt đơn giản¶

In [33]:
a = train.sort_values(["store_nbr", "family", "date"])
for i in [20, 30, 45, 60, 90, 120, 365, 730]:
    a["SMA"+str(i)+"_sales_lag16"] = a.groupby(["store_nbr", "family"]).rolling(i).sales.mean().shift(16).values
    a["SMA"+str(i)+"_sales_lag30"] = a.groupby(["store_nbr", "family"]).rolling(i).sales.mean().shift(30).values
    a["SMA"+str(i)+"_sales_lag60"] = a.groupby(["store_nbr", "family"]).rolling(i).sales.mean().shift(60).values
print("Correlation")
a[["sales"]+a.columns[a.columns.str.startswith("SMA")].tolist()].corr()
Correlation
Out[33]:
sales SMA20_sales_lag16 SMA20_sales_lag30 SMA20_sales_lag60 SMA30_sales_lag16 SMA30_sales_lag30 SMA30_sales_lag60 SMA45_sales_lag16 SMA45_sales_lag30 SMA45_sales_lag60 SMA60_sales_lag16 SMA60_sales_lag30 SMA60_sales_lag60 SMA90_sales_lag16 SMA90_sales_lag30 SMA90_sales_lag60 SMA120_sales_lag16 SMA120_sales_lag30 SMA120_sales_lag60 SMA365_sales_lag16 SMA365_sales_lag30 SMA365_sales_lag60 SMA730_sales_lag16 SMA730_sales_lag30 SMA730_sales_lag60
sales 1.00 0.91 0.89 0.87 0.91 0.89 0.87 0.91 0.90 0.87 0.91 0.89 0.87 0.91 0.89 0.87 0.90 0.89 0.86 0.90 0.89 0.87 0.91 0.89 0.86
SMA20_sales_lag16 0.91 1.00 0.99 0.95 1.00 0.98 0.95 0.99 0.98 0.95 0.99 0.98 0.95 0.99 0.98 0.95 0.98 0.97 0.95 0.97 0.97 0.95 0.98 0.98 0.95
SMA20_sales_lag30 0.89 0.99 1.00 0.96 0.99 1.00 0.96 1.00 0.99 0.97 0.99 0.99 0.97 0.99 0.99 0.96 0.99 0.98 0.96 0.97 0.97 0.96 0.98 0.98 0.96
SMA20_sales_lag60 0.87 0.95 0.96 1.00 0.96 0.98 1.00 0.98 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.98 0.98 0.97 0.97 0.98 0.98 0.98
SMA30_sales_lag16 0.91 1.00 0.99 0.96 1.00 0.99 0.96 1.00 0.99 0.96 0.99 0.99 0.96 0.99 0.98 0.96 0.99 0.98 0.96 0.98 0.97 0.96 0.98 0.98 0.96
SMA30_sales_lag30 0.89 0.98 1.00 0.98 0.99 1.00 0.98 1.00 1.00 0.98 1.00 0.99 0.98 0.99 0.99 0.97 0.99 0.99 0.97 0.98 0.98 0.97 0.98 0.98 0.98
SMA30_sales_lag60 0.87 0.95 0.96 1.00 0.96 0.98 1.00 0.98 0.99 1.00 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.98 0.98 0.98 0.98 0.98 0.98
SMA45_sales_lag16 0.91 0.99 1.00 0.98 1.00 1.00 0.98 1.00 1.00 0.98 1.00 0.99 0.98 0.99 0.99 0.98 0.99 0.99 0.97 0.98 0.98 0.97 0.98 0.98 0.98
SMA45_sales_lag30 0.90 0.98 0.99 0.99 0.99 1.00 0.99 1.00 1.00 0.99 1.00 1.00 0.99 1.00 0.99 0.98 0.99 0.99 0.98 0.98 0.98 0.98 0.98 0.98 0.98
SMA45_sales_lag60 0.87 0.95 0.97 0.99 0.96 0.98 1.00 0.98 0.99 1.00 0.99 0.99 1.00 0.99 1.00 0.99 0.99 0.99 0.99 0.98 0.98 0.98 0.98 0.98 0.98
SMA60_sales_lag16 0.91 0.99 0.99 0.99 0.99 1.00 0.99 1.00 1.00 0.99 1.00 1.00 0.99 1.00 0.99 0.98 0.99 0.99 0.98 0.98 0.98 0.98 0.98 0.98 0.98
SMA60_sales_lag30 0.89 0.98 0.99 0.99 0.99 0.99 0.99 0.99 1.00 0.99 1.00 1.00 0.99 1.00 1.00 0.99 1.00 0.99 0.98 0.98 0.98 0.98 0.98 0.98 0.98
SMA60_sales_lag60 0.87 0.95 0.97 0.99 0.96 0.98 0.99 0.98 0.99 1.00 0.99 0.99 1.00 0.99 1.00 1.00 1.00 1.00 0.99 0.99 0.99 0.98 0.98 0.98 0.98
SMA90_sales_lag16 0.91 0.99 0.99 0.99 0.99 0.99 0.99 0.99 1.00 0.99 1.00 1.00 0.99 1.00 1.00 0.99 1.00 1.00 0.99 0.99 0.99 0.98 0.99 0.98 0.98
SMA90_sales_lag30 0.89 0.98 0.99 0.99 0.98 0.99 0.99 0.99 0.99 1.00 0.99 1.00 1.00 1.00 1.00 0.99 1.00 1.00 0.99 0.99 0.99 0.98 0.98 0.99 0.98
SMA90_sales_lag60 0.87 0.95 0.96 0.99 0.96 0.97 0.99 0.98 0.98 0.99 0.98 0.99 1.00 0.99 0.99 1.00 1.00 1.00 1.00 0.99 0.99 0.99 0.98 0.98 0.99
SMA120_sales_lag16 0.90 0.98 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 0.99 1.00 1.00 1.00 1.00 1.00 1.00 1.00 0.99 0.99 0.99 0.99 0.99 0.99 0.99
SMA120_sales_lag30 0.89 0.97 0.98 0.99 0.98 0.99 0.99 0.99 0.99 0.99 0.99 0.99 1.00 1.00 1.00 1.00 1.00 1.00 1.00 0.99 0.99 0.99 0.99 0.99 0.99
SMA120_sales_lag60 0.86 0.95 0.96 0.98 0.96 0.97 0.99 0.97 0.98 0.99 0.98 0.98 0.99 0.99 0.99 1.00 0.99 1.00 1.00 0.99 0.99 0.99 0.99 0.99 0.99
SMA365_sales_lag16 0.90 0.97 0.97 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.99 0.99 0.99 0.99 0.99 0.99 0.99 1.00 1.00 1.00 1.00 1.00 1.00
SMA365_sales_lag30 0.89 0.97 0.97 0.97 0.97 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.99 0.99 0.99 0.99 0.99 0.99 0.99 1.00 1.00 1.00 1.00 1.00 1.00
SMA365_sales_lag60 0.87 0.95 0.96 0.97 0.96 0.97 0.98 0.97 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.99 0.99 0.99 0.99 1.00 1.00 1.00 1.00 1.00 1.00
SMA730_sales_lag16 0.91 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.99 0.98 0.98 0.99 0.99 0.99 1.00 1.00 1.00 1.00 1.00 1.00
SMA730_sales_lag30 0.89 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.99 0.98 0.99 0.99 0.99 1.00 1.00 1.00 1.00 1.00 1.00
SMA730_sales_lag60 0.86 0.95 0.96 0.98 0.96 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.98 0.99 0.99 0.99 0.99 1.00 1.00 1.00 1.00 1.00 1.00
In [34]:
b = a[(a.store_nbr == 1)].set_index("date")
for i in b.family.unique():
    fig, ax = plt.subplots(2,4,figsize=(20,10))
    b[b.family == i][["sales", "SMA20_sales_lag16"]].plot(legend = True, ax = ax[0,0], linewidth = 4)
    b[b.family == i][["sales", "SMA30_sales_lag16"]].plot(legend = True, ax = ax[0,1], linewidth = 4)
    b[b.family == i][["sales", "SMA45_sales_lag16"]].plot(legend = True, ax = ax[0,2], linewidth = 4)
    b[b.family == i][["sales", "SMA60_sales_lag16"]].plot(legend = True, ax = ax[0,3], linewidth = 4)
    b[b.family == i][["sales", "SMA90_sales_lag16"]].plot(legend = True, ax = ax[1,0], linewidth = 4)
    b[b.family == i][["sales", "SMA120_sales_lag16"]].plot(legend = True, ax = ax[1,1], linewidth = 4)
    b[b.family == i][["sales", "SMA365_sales_lag16"]].plot(legend = True, ax = ax[1,2], linewidth = 4)
    b[b.family == i][["sales", "SMA730_sales_lag16"]].plot(legend = True, ax = ax[1,3], linewidth = 4)
    plt.suptitle("STORE 1 - "+i, fontsize = 15)
    plt.tight_layout(pad = 1.5)
    for j in range(0,4):
        ax[0,j].legend(fontsize="x-large")
        ax[1,j].legend(fontsize="x-large")
    plt.show()

2.3.9. Đường trung bình trượt theo cấp số nhân¶

In [35]:
def ewm_features(dataframe, alphas, lags):
    dataframe = dataframe.copy()
    for alpha in alphas:
        for lag in lags:
            dataframe['sales_ewm_alpha_' + str(alpha).replace(".", "") + "_lag_" + str(lag)] = \
                dataframe.groupby(["store_nbr", "family"])['sales']. \
                    transform(lambda x: x.shift(lag).ewm(alpha=alpha).mean())
    return dataframe

alphas = [0.95, 0.9, 0.8, 0.7, 0.5]
lags = [16, 30, 60, 90]

a = ewm_features(a, alphas, lags)
In [36]:
a[(a.store_nbr == 1) & (a.family == "GROCERY I")].set_index("date")[["sales", "sales_ewm_alpha_095_lag_16"]].plot(title = "STORE 1 - GROCERY I");